package main

import (
	"encoding/json"
	"fmt"
	"github.com/urfave/cli"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"server/com/util"
	"text/template"
)

var (
	global   = []string{"service", "bill", "center", "account", "gate", "manage", "log"}
	oneWorld = []string{"game"}
	dep      = []string{"nsqd", "admin", "lookup"}
	svc      = []string{"svc"}
)

func main() {
	app := &cli.App{
		Name:  "make k8s yaml",
		Usage: "make k8s yaml",
		Flags: []cli.Flag{
			cli.IntFlag{Name: "w", Usage: "--w=", Value: 1},
		},
		Action: Action,
	}
	err := app.Run(os.Args)
	if err != nil {
		fmt.Println("err")
		return
	}
}

func parser(ctx *cli.Context) map[string]interface{} {
	b, err := ioutil.ReadFile("values.yaml")
	if err != nil {
		log.Printf("read values.yaml err:%v", err)
		return nil
	}
	data := make(map[string]interface{})
	err = yaml.Unmarshal(b, data)
	if err != nil {
		log.Printf("unmarshal values.yaml err:%v", err)
		return nil
	}

	return data
}

func createPathIfNotExist(path string) {
	if _, err := os.Stat(path); err != nil {
		if os.IsNotExist(err) {
			err = os.Mkdir(path, os.ModePerm)
			if err != nil {
				fmt.Printf("create path %s err", path)
				return
			}
		}
	}
}

func TruncateAndSeek(f *os.File) error {
	err := f.Truncate(0)
	if err != nil {
		return err
	}
	_, err = f.Seek(0, 0)
	return err
}

func Action(ctx *cli.Context) error {
	v := parser(ctx)
	worlds := make([]string, 0)
	worldCnt := ctx.Int("w")
	for wNO := 1; wNO <= worldCnt; wNO++ {
		worlds = append(worlds, util.ToString(wNO))
	}

	if err := makeShell(v, worlds, "../"+"quick_start.sh",
		"./template/start_global.sh", "./template/start_one_world.sh"); err != nil {
		return err
	}
	if err := makeShell(v, worlds, "../"+"quick_stop.sh",
		"./template/stop_global.sh", "./template/stop_one_world.sh"); err != nil {
		return err
	}
	if err := makeShell(v, worlds, "../"+"start.sh",
		"./template/start_all_global.sh", "./template/start_one_world.sh"); err != nil {
		return err
	}
	if err := makeShell(v, worlds, "../"+"stop.sh",
		"./template/stop_all_global.sh", "./template/stop_one_world.sh"); err != nil {
		return err
	}

	for _, ser := range global {
		err := makeFile("../", ser, v)
		if err != nil {
			return err
		}
	}

	for _, worldNum := range worlds {
		path := "../w" + worldNum
		createPathIfNotExist(path)
		for _, ws := range oneWorld {
			v["World"] = worldNum
			err := makeFile(path, ws, v)
			if err != nil {
				return err
			}
		}
	}

	//dep
	path := "../dep"
	createPathIfNotExist(path)
	for _, name := range dep {
		err := makeFile(path, name, v)
		if err != nil {
			return err
		}
	}
	for _, s := range svc {
		err := makeFile(path, s, v)
		if err != nil {
			return err
		}
	}

	v["dbCfg"] = jsonIndent2Str(v["dbCfg"].(string))
	v["initCfg"] = jsonIndent2Str(v["initCfg"].(string))
	//v["serverList"] = jsonIndent2Str(v["serverList"].(string))
	err := makeFile(path, "config", v)
	if err != nil {
		return err
	}

	return nil
}

func makeShell(v map[string]interface{}, worlds []string, outName string, globalTempPathName string, worldTempPathName string) error {
	out, err := os.OpenFile(outName, os.O_CREATE|os.O_WRONLY, 0755)
	if err != nil {
		log.Printf("Open %s err:%v", outName, err)
		return err
	}
	defer out.Close()
	if err = TruncateAndSeek(out); err != nil {
		return err
	}
	startTemp, err := template.ParseFiles(globalTempPathName)
	if err != nil {
		log.Printf("%s parse file err:%v", globalTempPathName, err)
		return err
	}
	err = startTemp.Execute(out, v)
	if err != nil {
		return err
	}
	log.Printf("make %s success", globalTempPathName)

	startOneWorldTemp, err := template.ParseFiles(worldTempPathName)
	if err != nil {
		log.Printf("%s parse file err:%v", worldTempPathName, err)
		return err
	}

	for _, worldNum := range worlds {
		_, err = out.WriteString("\n#world " + worldNum + "\n")
		if err != nil {
			return err
		}
		v["World"] = worldNum
		err = startOneWorldTemp.Execute(out, v)
		if err != nil {
			return err
		}
		log.Printf("make %s success", worldTempPathName)
	}

	return err
}

func makeFile(outPath string, name string, v map[string]interface{}) error {
	templateName := "./template/" + name + ".yaml"
	t, err := template.ParseFiles(templateName)
	if err != nil {
		log.Printf("template %s parse file err:%v", templateName, err)
		return err
	}

	outPutFileName := outPath + "/" + name + ".yaml"
	out, err := os.OpenFile(outPutFileName, os.O_CREATE|os.O_WRONLY, 0666)
	if err != nil {
		log.Printf("open file %s for out put err:%v", outPutFileName, err)
		return err
	}
	err = out.Truncate(0)
	if err != nil {
		log.Printf("tran cate file %s for out put err:%v", outPutFileName, err)
		return err
	}
	_, err = out.Seek(0, 0)
	if err != nil {
		log.Printf("seek file %s for out put err:%v", outPutFileName, err)
		return err
	}

	defer out.Close()

	err = t.Execute(out, v)
	if err != nil {
		return err
	}
	log.Printf("make %s success", outPutFileName)
	return err
}

func jsonIndent2Str(indent string) string {
	var data interface{}
	err := json.Unmarshal([]byte(indent), &data)
	if err != nil {
		log.Printf("%s unmarshal err:%v", indent, err)
		return ""
	}
	b, err := json.Marshal(data)
	if err != nil {
		log.Printf("%s marshal err:%v", data, err)
		return ""
	}
	return string(b)
}
